#pragma once

#include <type_traits>

namespace limbo::details {

template <typename T, typename = void> struct LowerStateImpl {
  using type = void;
};

template <typename T>
struct LowerStateImpl<T, std::void_t<typename T::LowerState>> {
  using type = typename T::LowerState;
};

template <typename T>
constexpr bool has_lower_state_v =
    !std::is_same_v<typename LowerStateImpl<T>::type, void>;

template <typename T> struct LowerPacketImpl {
  using type = typename T::Packet;
};

template <> struct LowerPacketImpl<void> { using type = void; };

template <typename T> using lower_packet_t = typename LowerPacketImpl<T>::type;

template <typename T, bool> struct LowerContextImpl;

template <typename T> struct LowerContextImpl<T, true> {
  using type = typename T::LowerState::Context;
};

template <typename T> struct LowerContextImpl<T, false> { using type = void; };

template <typename T>
using lower_context_t =
    typename LowerContextImpl<T, has_lower_state_v<T>>::type;

} // namespace limbo::details
